package com.seepln.connect.impl;
/**
 * Created by
 *
 * @auth: liqy
 * on 2021/4/20 10:48.
 * desc:
 */

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.sql.SqlExecutor;
import cn.hutool.db.sql.SqlLog;
import cn.hutool.log.level.Level;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.seepln.modal.DBProtocol;
import com.seepln.modal.Protocol;
import org.seepln.connect.Connection;
import org.seepln.connect.RequestDataObject;
import org.seepln.connect.ResponseDataObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @ClassName: MySQLConnector
 * @Description: TODO
 * @Author: liqy
 * @CreateDate: 2021/4/20 10:48
 * @UpdateUser: liqy
 * @UpdateDate: 2021/4/20  10:48
 * @UpdateRemark: update TODO
 * @Version: 1.0
 */
public class DBConnector implements Connection {
    private static final Logger log = LoggerFactory.getLogger(DBConnector.class);

    private Protocol protocol;

    private DBProtocol params;

    private Db db;

    private SqlLog SQLLOG = SqlLog.INSTANCE;

    private static final Boolean AUTO_COMMIT = true;
    private static final Boolean KILL_WHEN_SOCKET_READTIMEOUT = true;
    private static final String CONNECTION_TEST_QUERY = "select 1";
    private static final Integer CONNECTION_TIMEOUT = 3;
    private static final Integer REMOVE_ABANDONED_TIMEOUT = 6;
    private static final long IDLE_TIMEOUT = 3000;
    private static final Boolean ISOLATE_INTERNAL_QUERIES = false;
    private static final int MAXIMUM_POOLSIZE = 3000;
    private static final long MAXLIFETIME = 30000;
    private static final int MINIMUMIDLE = 10;
    private static final String POOLNAME = "druidPool";
    private static final String encoding = "utf-8";

    private static final String DBTYPE = "mysql";
    private static final String VERSION = "8.0.13";

    /**
     * 双验证单例模式
     */
    private static volatile DruidDataSource druidDataSource;

    public DBConnector(Protocol protocol) throws Exception {
        this.protocol = protocol;
        SQLLOG.init(true, true, true, Level.DEBUG);
        params = JSON.parseObject(protocol.getConfig(), DBProtocol.class);
        start();
    }

    @Override
    public void start() {
        setDataSource(params);
        db = Db.use(druidDataSource);
    }

    /**
     * 配置sqlSessionFactory的方法
     *
     * @param params
     */
    public void setDataSource(DBProtocol params) {
        String classPath = this.getClass().getClassLoader().getResource("").getPath();
        String libPath = classPath.substring(0, classPath.lastIndexOf("classes")) + "lib";
        // 创建基础Druid数据源
        ClassLoader myDBClassLoader = new DBClassLoaderFactory(libPath).getDbClassLoader(Objects.nonNull(params.getType()) ? params.getType() : DBTYPE, Objects.nonNull(params.getVersion()) ? params.getVersion() : VERSION);
        DataSourceBuilder<DruidDataSource> druidDataSourceBuilder = DataSourceBuilder.create(myDBClassLoader).type(DruidDataSource.class);
        druidDataSource = druidDataSourceBuilder.driverClassName(params.getDriver()).url(params.getUrl()).username(params.getUser()).password(params.getPass()).build();

        //配置Druid连接池
        druidDataSource.setDriverClassLoader(myDBClassLoader);
        druidDataSource.setDefaultAutoCommit(Objects.nonNull(params.getAutoCommit()) ? params.getAutoCommit() : AUTO_COMMIT);//update自动提交设置
        druidDataSource.setValidationQuery(Objects.nonNull(params.getConnectionTestQuery()) ? params.getConnectionTestQuery() : CONNECTION_TEST_QUERY);//连接查询语句设置
        druidDataSource.setQueryTimeout(Objects.nonNull(params.getConnectionTimeout()) ? params.getConnectionTimeout() : CONNECTION_TIMEOUT);//连接超时时间设置
        druidDataSource.setValidationQueryTimeout(Objects.nonNull(params.getConnectionTimeout()) ? params.getConnectionTimeout() : CONNECTION_TIMEOUT);//连接超时时间设置
        druidDataSource.setKillWhenSocketReadTimeout(KILL_WHEN_SOCKET_READTIMEOUT);
        druidDataSource.setRemoveAbandonedTimeout(REMOVE_ABANDONED_TIMEOUT);
        druidDataSource.setMaxActive(Objects.nonNull(params.getMaximumPoolSize()) ? params.getMaximumPoolSize() : MAXIMUM_POOLSIZE);//连接池允许的最大连接数量
        druidDataSource.setMaxWait(Objects.nonNull(params.getMaxLifetime()) ? params.getMaxLifetime() : MAXLIFETIME);//检查空余连接优化连接池设置时间,单位毫秒
        druidDataSource.setMinIdle(Objects.nonNull(params.getMinimumIdle()) ? params.getMinimumIdle() : MINIMUMIDLE);//连接池保持最小空余连接数量
        druidDataSource.setName(Objects.nonNull(params.getPoolName()) ? params.getPoolName() : POOLNAME);//连接池名称
    }

    @Override
    public void stop() {
    }

    @Override
    public ResponseDataObject call(RequestDataObject requestDataObject) throws Exception {
        Object requestMessage = requestDataObject.getRequestMessage();
        Map<String, Object> requestParams = requestDataObject.getRequestParams();
        if (MapUtil.isNotEmpty(requestParams)
                && requestParams.containsKey("sql")
                && StrUtil.isNotBlank((String) requestParams.get("sql"))
                && requestParams.containsKey("type")
                && StrUtil.isNotBlank((String) requestParams.get("type"))) {
            String sql = (String) requestParams.get("sql");
            if (log.isDebugEnabled()) {
                log.debug("type:{} \t sql:{} \t params:{}", requestParams.get("type"), sql, JSON.toJSONString(requestMessage));
            }
            if ("query".equalsIgnoreCase((String) requestParams.get("type"))) {
                Object[] params = new Object[0];
                if (Objects.nonNull(requestMessage)) {
                    params = ((String) requestMessage).split(",");
                }
                List<Entity> query = db.query(sql, params);
                if (log.isDebugEnabled()) {
                    log.debug("type:query \t sql:{} \t params:{} \t response:{}", sql, JSON.toJSONString(requestMessage), query);
                }
                if (requestParams.containsKey("action")) {
                    return ResponseDataObject
                            .builder()
                            .fetchDataMessage(query)
                            .build();
                }
                return ResponseDataObject
                        .builder()
                        .responseMessage(query)
                        .build();
            } else {
                int[] execute = new int[0];
                if (ArrayUtil.isArray(requestMessage)) {
                    execute = new int[]{db.execute(sql, requestMessage)};
                } else if (requestMessage instanceof List) {
                    java.sql.Connection conn = null;
                    try {
                        conn = this.db.getConnection();
                        execute = SqlExecutor.executeBatch(conn, sql, (List) requestMessage);
                    } finally {
                        this.db.closeConnection(conn);
                    }
                }
                if (log.isDebugEnabled()) {
                    log.debug("type:exec \t sql:{} \t params:{} \t response:{}", sql, JSON.toJSONString(requestMessage), execute);
                }
                if (requestParams.containsKey("action")) {
                    return ResponseDataObject
                            .builder()
                            .fetchDataMessage(execute)
                            .build();
                }
                return ResponseDataObject
                        .builder()
                        .responseMessage(execute)
                        .build();
            }
        }
        return ResponseDataObject
                .builder()
                .build();
    }
}
